// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "WW3DShape.h"
#import "RIBPatchMesh.h"

@implementation RIBPatchMesh

+ initialize { return [RIBPatchMesh setVersion:1], self; }

- (BOOL)hasBoundingBox { return YES; }

- init
{
  [super init];

  type = NULL;
  return self;
}


- copyFromZone:(NXZone *)zone
{
   id   newCopy = [super copyFromZone:zone];

  NXLogError("WARNING: copyFromZone: incompletely implemented for %s : I'm leaking memory in -free", [[self class] name]);

  return newCopy;
}


- setType:(RtToken)newType nU:(RtInt)newNU uWrap:(RtToken)newUWrap nV:(RtInt)newNV vWrap:(RtToken)newVWrap 
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   type = NXCopyStringBuffer(newType);
   nU = newNU;
   uWrap = NXCopyStringBuffer(newUWrap);
   nV = newNV;
   vWrap = NXCopyStringBuffer(newVWrap);
  
   [self setN:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setType:(RtToken)newType  {  if (type) { free(type); }  type = NXCopyStringBuffer(newType); return self; } 
- (RtToken)type { return type; }
- setNU:(RtInt)newNU  {  nU = newNU; return self; } 
- (RtInt)nU { return nU; }
- setUWrap:(RtToken)newUWrap  {  if (uWrap) { free(uWrap); }  uWrap = NXCopyStringBuffer(newUWrap); return self; } 
- (RtToken)uWrap { return uWrap; }
- setNV:(RtInt)newNV  {  nV = newNV; return self; } 
- (RtInt)nV { return nV; }
- setVWrap:(RtToken)newVWrap  {  if (vWrap) { free(vWrap); }  vWrap = NXCopyStringBuffer(newVWrap); return self; } 
- (RtToken)vWrap { return vWrap; }
- (RtBasis *)uBasis  {  return &uBasis; }
- (RtToken)uBasisToken  {  return uBasisToken; }
- (RtInt)uStep  {  return uStep; }
- (RtBasis *)vBasis  {  return &vBasis; }
- (RtToken)vBasisToken  {  return vBasisToken; }
- (RtInt)vStep  {  return vStep; }

// note that the code in here is largely stolen from WW3DShape's updateBases 
// function.  The reason we can't use it directly is because of the need to 
// start (potentially) somewhere in the middle of the list of ribCommands.
- brokenGetBases 
{
  int   ourPosition = -1, 
        howManyPositions, i;
  BOOL  foundIt;
  id    cmd = nil, 
        ancestor = [myShape ancestor];  


  howManyPositions = [[myShape ribCommands] count];
  i = 0;
  foundIt = NO;
  while (!foundIt && (i < howManyPositions))
  {  if (self == [[myShape ribCommands] objectAt:i])
     {  ourPosition = i;
        foundIt = YES;
     }
     else
     {  i++;
     }
  }
  if (!foundIt)
  {  NXLogError("Yikes! couldn't find myself inside myShape (%d)", myShape);
     return nil;
  }

  i = (ourPosition - 1);
  foundIt = NO;
  while (!foundIt && (i > 0))
  {  cmd = [[myShape ribCommands] objectAt:i];
     if ([cmd respondsTo:@selector(uBasis)])
     {  foundIt = YES;
     }
     else
     {  i--;
     }
  }

  if (foundIt)
  {  N3D_CopyMatrix(*([cmd uBasis]), uBasis);
     uStep = [cmd uStep];
     N3D_CopyMatrix(*([cmd vBasis]), vBasis);
     vStep = [cmd vStep];
  }
  else
  {  if (ancestor)  // get it from our ancestor
     {  N3D_CopyMatrix(*([ancestor uBasis]), uBasis);
        uStep = [ancestor uStep];
        N3D_CopyMatrix(*([ancestor vBasis]), vBasis);
        vStep = [ancestor vStep];
     }
     else // the shape we are in is the root; fill in bezier bases for both u and v
     {  N3D_CopyMatrix(RiBezierBasis, uBasis);
        uStep = 3;
        N3D_CopyMatrix(RiBezierBasis, vBasis);
        vStep = 3;
     }
  }


  return self;
}

- getBases 
{
  //WAVE - fix ME!!! boge for now
  N3D_CopyMatrix(RiBezierBasis, uBasis);
  uStep = 3;
  N3D_CopyMatrix(RiBezierBasis, vBasis);
  vStep = 3;

  return self;
}


- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  if (![self getBases])
  {  return nil;
  }

  // if both are bezier, just call our super's routine, since we 
  // can just check against the points.  

  // ya know, we might want to have a string tag along with the bases,
  // just so we don't have to deal with this epsilon bullshit...

  if (   (uBasisToken && vBasisToken) 
      && (   ((!strcmp(uBasisToken, RI_BEZIER)) && (!strcmp(vBasisToken, RI_BEZIER)))
          || ((!strcmp(uBasisToken, RI_BSPLINE)) && (!strcmp(vBasisToken, RI_BSPLINE)))
          || ((!strcmp(uBasisToken, RI_BEZIER)) && (!strcmp(vBasisToken, RI_BSPLINE)))
          || ((!strcmp(uBasisToken, RI_BSPLINE)) && (!strcmp(vBasisToken, RI_BEZIER)))))
  {
    [super calculateBoundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime];
    dirtyBoundingBox = FALSE; 
    return self;
  }

  // otherwise, need to transform one or both...
  // for now, we'll still bail, and just call the super version...
  [super calculateBoundingBoxStartingAt:shutterOpenTime endingAt:shutterCloseTime];
  dirtyBoundingBox = FALSE; 

  return self;
}


- (BOOL)theSameAs:otherRIBCommand 
{ 
  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }
  if (type != [(RIBPatchMesh *)otherRIBCommand type])  // if they're tokens, they're the same string pointer
  {  if (strcmp(type, [(RIBPatchMesh *)otherRIBCommand type])) // just make sure by comparing the actual strings....
     {  return NO; // okay, they really are different
     }
  }

  if (nU != [otherRIBCommand nU])  
  {  return NO;
  }
  if (uWrap != [otherRIBCommand uWrap])  // if they're tokens, they're the same string pointer
  {  if (strcmp(uWrap, [otherRIBCommand uWrap])) // just make sure by comparing the actual strings....
     {  return NO; // okay, they really are different
     }
  }

  if (nV != [otherRIBCommand nV])  
  {  return NO;
  }
  if (vWrap != [otherRIBCommand vWrap])  // if they're tokens, they're the same string pointer
  {  if (strcmp(vWrap, [otherRIBCommand vWrap])) // just make sure by comparing the actual strings....
     {  return NO; // okay, they really are different
     }
  }

  // this will check all the parameters...
  return [super theSameAs:otherRIBCommand];
}


- (BOOL)similarTo:otherRIBCommand 
{
  if ([self class] != [otherRIBCommand class])
  {  return NO;
  }
  if (nU != [otherRIBCommand nU])  
  {  return NO;
  }
  if (uWrap != [otherRIBCommand uWrap])  // if they're tokens, they're the same string pointer
  {  if (strcmp(uWrap, [otherRIBCommand uWrap])) // just make sure by comparing the actual strings....
     {  return NO; // okay, they really are different
     }
  }

  if (nV != [otherRIBCommand nV])  
  {  return NO;
  }
  if (vWrap != [otherRIBCommand vWrap])  // if they're tokens, they're the same string pointer
  {  if (strcmp(vWrap, [otherRIBCommand vWrap])) // just make sure by comparing the actual strings....
     {  return NO; // okay, they really are different
     }
  }
  return YES;
}


- renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  RiPatchMeshV(type, nU, uWrap, nV, vWrap, n, tokens, parms);

  return self;
}

- (BOOL)isMotionBlurrable { return YES; }

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "PatchMesh %s %d %s %d %s ", type, nU, uWrap, nV, vWrap);
   [super writeParameterList:stream];
   return self;
}

#define typeVector "*i*i*"
#define typeValues &type, &nU, &uWrap, &nV, &vWrap

- read:(NXTypedStream*)stream 
{
    int version;
    [super read:stream];

    version = NXTypedStreamClassVersion(stream,"RIBPatchMesh");
    if (version == 0) NXReadTypes(stream,"i",&version), version=1;
    if (version == 1)
    {  NXReadTypes(stream,typeVector,typeValues);
    } 
    else 
    {
    }
    return self;
}

- write:(NXTypedStream*)stream 
{
    [super write:stream];

    NXWriteTypes(stream,typeVector, typeValues);

    return self;
}


@end
